package com.innovation.ic.b1b.monitor.base.handler;

import com.google.common.base.Strings;
import com.innovation.ic.b1b.framework.manager.SftpChannelManager;
import com.innovation.ic.b1b.monitor.base.mapper.*;
import com.innovation.ic.b1b.monitor.base.model.*;
import com.innovation.ic.b1b.monitor.base.pojo.ServerMemoryInfoPojo;
import com.innovation.ic.b1b.monitor.base.pojo.ServerSpacePojo;
import com.innovation.ic.b1b.monitor.base.pojo.constant.Constant;
import com.innovation.ic.b1b.monitor.base.pojo.constant.JobDataMapConstant;
import com.innovation.ic.b1b.monitor.base.pojo.constant.LinuxCommandConstant;
import com.innovation.ic.b1b.monitor.base.pojo.constant.ServerPerformanceJobConstant;
import com.innovation.ic.b1b.monitor.base.value.ServerPerformanceConfig;
import lombok.SneakyThrows;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.quartz.QuartzJobBean;
import javax.annotation.Resource;
import java.io.IOException;
import java.lang.System;
import java.math.BigDecimal;
import java.util.*;

/**
 * @desc   服务器性能任务处理类
 * @author linuo
 * @time   2023年3月22日14:49:36
 */
public class ServerPerformanceJobHandler extends QuartzJobBean {
    private Logger log = LoggerFactory.getLogger(this.getClass());

    @Resource
    private ServerPerformanceConfig serverPerformanceConfig;

    @Resource
    private HandlerHelper handlerHelper;

    @Resource
    protected ServerPerformanceJobMapper serverPerformanceJobMapper;

    @Resource
    protected ServerPerformanceJobLogMapper serverPerformanceJobLogMapper;

    @Resource
    protected ServerPerformanceJobLogDetailMapper serverPerformanceJobLogDetailMapper;

    @Resource
    protected ServerMapper serverMapper;

    @SneakyThrows
    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) {
        log.info("开始执行服务器性能监控任务");

        // 获取1-5间的随机数
        int min = 1;
        int max = 5;
        Random random = new Random();
        int s = random.nextInt(max) % (max - min + 1) + min;
        int sleepTime = s * 1000;
        log.info("休眠[{}]秒", s);
        Thread.sleep(sleepTime);

        JobDataMap jobDataMap = jobExecutionContext.getMergedJobDataMap();
        int id = jobDataMap.getInt(JobDataMapConstant.ID);
        ServerPerformanceJob serverPerformanceJob = serverPerformanceJobMapper.selectById(id);
        if(serverPerformanceJob != null){
            Server server = serverMapper.selectById(serverPerformanceJob.getServerId());
            if(server != null){
                // 是否发送邮件
                boolean ifSendEmail = Boolean.FALSE;

                // 服务器性能任务日志详情数据
                List<ServerPerformanceJobLogDetail> data = new ArrayList<>();

                // 组装服务器性能任务日志表数据
                ServerPerformanceJobLog serverPerformanceJobLog = new ServerPerformanceJobLog();
                serverPerformanceJobLog.setServerPerformanceJobId(id);

                String userName = server.getUserName();
                String password = server.getPassword();
                String ip = server.getIp();
                Integer sshPort = server.getSshPort();

                if(Strings.isNullOrEmpty(userName) || Strings.isNullOrEmpty(password) || Strings.isNullOrEmpty(ip) || sshPort == null){
                    String message = "server配置信息不完整,无法执行任务,serverId=" + server.getId();
                    log.info(message);
                    serverPerformanceJobLog.setDescription(message);
                }else{
                    try {
                        SftpChannelManager sftpChannelManager = new SftpChannelManager(userName, password, ip, sshPort);

                        // 执行linux命令获取磁盘情况
                        List<String> serverDiskStatus = sftpChannelManager.execCommand(LinuxCommandConstant.CHECK_SERVER_DISK_STATUS);
                        if(serverDiskStatus != null && serverDiskStatus.size() > 0){

                            // 处理执行结果
                            List<ServerSpacePojo> list = disposeFilesSystem(serverDiskStatus);
                            if(list.size() > 0){

                                // 获取磁盘占用百分比报警阈值
                                Integer diskWarningPercentage = serverPerformanceConfig.getDiskWarningPercentage();

                                StringBuilder sb = new StringBuilder();
                                sb.append("文件系统：");
                                for(ServerSpacePojo serverSpacePojo : list){
                                    String percentageSpent = serverSpacePojo.getPercentageSpent();
                                    percentageSpent = percentageSpent.replaceAll("%", "");
                                    if(Integer.parseInt(percentageSpent) >= diskWarningPercentage){
                                        ifSendEmail = Boolean.TRUE;
                                        sb.append(serverSpacePojo.getFileSystem())
                                                .append(" 的磁盘空间占用已达到")
                                                .append(percentageSpent)
                                                .append("%")
                                                .append(Constant.PAUSE);
                                    }

                                    // 组装服务器性能任务日志详情表数据
                                    ServerPerformanceJobLogDetail serverPerformanceJobLogDetail = new ServerPerformanceJobLogDetail();
                                    serverPerformanceJobLogDetail.setFileSystem(serverSpacePojo.getFileSystem());
                                    serverPerformanceJobLogDetail.setUsedDiskSpacePercentage(Double.parseDouble(percentageSpent));
                                    serverPerformanceJobLogDetail.setMountPoint(serverSpacePojo.getMountPoint());
                                    data.add(serverPerformanceJobLogDetail);
                                }

                                if(ifSendEmail){
                                    String message = sb.toString();
                                    if(message.endsWith(Constant.PAUSE)){
                                        message = message.substring(0, message.length() - 1);
                                    }
                                    serverPerformanceJobLog.setDescription(message);
                                }
                            }
                        }

                        // 获取已用内存百分比
                        Thread.sleep(2000);
                        sftpChannelManager.closeChannel();
                        sftpChannelManager = new SftpChannelManager(userName, password, ip, sshPort);

                        // 已用内存最大百分比
                        Double usedRamPercentage = getUsedRamPercentage(sftpChannelManager);
                        serverPerformanceJobLog.setUsedRamPercentage(usedRamPercentage);

                        // 判断当前机器内存占用是否超了预警值
                        Integer memoryWarningPercentage = serverPerformanceConfig.getMemoryWarningPercentage();
                        if(usedRamPercentage != null && usedRamPercentage >= memoryWarningPercentage.doubleValue()){
                            ifSendEmail = Boolean.TRUE;

                            String description = serverPerformanceJobLog.getDescription();
                            if(!Strings.isNullOrEmpty(description)){
                                description = description + Constant.PAUSE + "内存占用已达到" + usedRamPercentage + "%";
                            }else{
                                description = "内存占用已达到" + usedRamPercentage + "%";
                            }
                            serverPerformanceJobLog.setDescription(description);
                        }

                        // 获取已用磁盘空间最大百分比
                        if(data.size() > 0){
                            List<Double> gameIdList = new ArrayList<>();
                            for (ServerPerformanceJobLogDetail serverPerformanceJobLogDetail : data) {
                                Double usedDiskSpacePercentage = serverPerformanceJobLogDetail.getUsedDiskSpacePercentage();
                                gameIdList.add(usedDiskSpacePercentage);
                            }

                            // 已用磁盘空间最大百分比
                            Double usedDiskSpaceMaxPercentage = Collections.max(gameIdList);
                            serverPerformanceJobLog.setUsedDiskSpaceMaxPercentage(usedDiskSpaceMaxPercentage);
                        }
                    }catch (Exception e){
                        log.warn("监控服务器性能任务功能出现问题,原因:", e);
                        String message = e.getMessage();
                        if(!Strings.isNullOrEmpty(message)){
                            if(message.length() > 100){
                                message = message.substring(0, 100);
                            }
                            serverPerformanceJobLog.setDescription(message);
                        }
                    }
                }

                serverPerformanceJobLog.setStartTime(new Date(System.currentTimeMillis()));
                int insert = serverPerformanceJobLogMapper.insert(serverPerformanceJobLog);
                if(insert > 0){
                    log.info("服务器性能任务日志数据插入成功");

                    if(ifSendEmail){
                        // 发送邮件
                        String message = "您的 " + server.getName() + " 机器在" + serverPerformanceJobLog.getDescription() + "，请查看服务器情况并及时进行处理。";
                        handlerHelper.sendEmail(serverPerformanceJob.getAlarmEmail(), message, null);
                    }

                    if(data.size() > 0){
                        for (ServerPerformanceJobLogDetail serverPerformanceJobLogDetail : data) {
                            serverPerformanceJobLogDetail.setServerPerformanceJobLogId(serverPerformanceJobLog.getId());
                        }
                        Integer integer = serverPerformanceJobLogDetailMapper.insertBatchSomeColumn(data);
                        if(integer > 0){
                            log.info("服务器性能任务日志详情数据插入成功");
                        }
                    }
                }
            }else{
                log.info("服务器id为[{}]的配置信息不存在,无法执行任务", serverPerformanceJob.getServerId());
            }
        }else{
            log.info("服务器性能监控任务id为[{}]的数据不存在,无法执行任务", id);
        }
    }

    /**
     * 获取已用内存百分比
     * @param sftpChannelManager 服务器连接信息
     * @return 返回结果
     */
    private Double getUsedRamPercentage(SftpChannelManager sftpChannelManager) throws IOException {
        double result = 0D;

        // 执行linux命令查看内存占用情况
        List<String> memoryUsage = sftpChannelManager.execCommand(LinuxCommandConstant.VIEW_MEMORY_USAGE);
        if(memoryUsage != null && memoryUsage.size() > 0){
            List<ServerMemoryInfoPojo> data = new ArrayList<>();

            log.info("执行命令[{}]的结果为:", LinuxCommandConstant.VIEW_MEMORY_USAGE);
            for (String info : memoryUsage) {
                // 删除字符串中的多余空格
                info = deleteBlank(info);
                log.info(info);
                String[] s = info.split(" ");
                if (s.length == 7 && (ServerPerformanceJobConstant.MEMORY_TYPE.equals(s[0]) || ServerPerformanceJobConstant.SWAP_MEMORY_TYPE.equals(s[0]))) {
                    ServerMemoryInfoPojo serverMemoryInfoPojo = new ServerMemoryInfoPojo();
                    serverMemoryInfoPojo.setType(s[0]);
                    serverMemoryInfoPojo.setTotal(new BigDecimal(s[1]));
                    serverMemoryInfoPojo.setUsed(new BigDecimal(s[2]));
                    serverMemoryInfoPojo.setFree(new BigDecimal(s[3]));
                    serverMemoryInfoPojo.setShared(new BigDecimal(s[4]));
                    serverMemoryInfoPojo.setBuffOrCache(new BigDecimal(s[5]));
                    serverMemoryInfoPojo.setAvailable(new BigDecimal(s[6]));
                    data.add(serverMemoryInfoPojo);
                }
            }

            if(data.size() > 0){
                for(ServerMemoryInfoPojo serverMemoryInfoPojo : data){
                    if(ServerPerformanceJobConstant.MEMORY_TYPE.equals(serverMemoryInfoPojo.getType())){
                        BigDecimal used = serverMemoryInfoPojo.getUsed();
                        BigDecimal total = serverMemoryInfoPojo.getTotal();

                        // 计算内存占用百分比
                        BigDecimal divide = used.divide(total,4,BigDecimal.ROUND_HALF_UP);
                        BigDecimal multiply = divide.multiply(BigDecimal.valueOf(100));
                        result = multiply.doubleValue();
                    }
                }
            }
        }

        return result;
    }

    /**
     * 处理执行结果
     * @param executeLog 处理系统磁盘状态shell执行结果
     * @return 返回处理后的结果
     */
    private List<ServerSpacePojo> disposeFilesSystem(List<String> executeLog) {
        log.info("执行命令[{}]的结果为:", LinuxCommandConstant.CHECK_SERVER_DISK_STATUS);

        List<ServerSpacePojo> list = new ArrayList<>();

        for (int i = 0; i < executeLog.size(); i++) {
            if(i < 8){
                continue;
            }

            String info = executeLog.get(i);
            if(!Strings.isNullOrEmpty(info)){

                // 删除字符串中的多余空格
                info = deleteBlank(info);
                log.info(info);

                String[] s = info.split(" ");
                if(s.length == 6){
                    ServerSpacePojo serverSpacePojo = new ServerSpacePojo();
                    serverSpacePojo.setFileSystem(s[0]);
                    serverSpacePojo.setCapacity(s[1]);
                    serverSpacePojo.setUsed(s[2]);
                    serverSpacePojo.setAvailable(s[3]);
                    serverSpacePojo.setPercentageSpent(s[4]);
                    serverSpacePojo.setMountPoint(s[5]);
                    list.add(serverSpacePojo);
                }
            }
        }
        return list;
    }

    /**
     * 删除字符串中的多余空格
     * @param string 要处理
     * @return 返回处理后的结果
     */
    private String deleteBlank(String string){
        StringBuilder sb = new StringBuilder();

        for(int i = 0; i < string.length(); i++){
            if(string.charAt(i) != ' '){
                sb.append(string.charAt(i));
            }
            try {
                if(string.charAt(i) == ' ' && string.charAt(i+1) != ' '){
                    sb.append(' ');
                }
            } catch (Exception ignored) {}
        }

        return sb.toString();
    }
}